#include <ctype.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>

#include <algorithm>
#include <map>
#include <string>

#include <AboutWindow.h>
#include <AppDefs.h>
#include <AppFileInfo.h>
#include <Application.h>
#include <Bitmap.h>
#include <ColorConversion.h>
#include <ControlLook.h>
#include <DateTimeFormat.h>
#include <Dragger.h>
#include <DurationFormat.h>
#include <File.h>
#include <FindDirectory.h>
#include <Font.h>
#include <fs_attr.h>
#include <LayoutBuilder.h>
#include <MessageRunner.h>
#include <Messager.h>
#include <NumberFormat.h>
#include <ObjectList.h>
#include <OS.h>
#include <Path.h>
#include <PathFinder.h>
#include <PopUpMenu.h>

#include <Resources.h>
#include <Screen.h>
#include <ScrollView.h>
#include <String.h>
#include <StringFormat.h>
#include <StringList.h>
#include <StringView.h>
#include <TextView.h>

#include <TranslationUtils.h>
#include <TranslatorFormats.h>
#include <View.h>
#include <ViewPrivate.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <Window.h>
#include <WindowPrivate.h>

#include <AppMisc.h>
#include <AutoDeleter.h>
#include <AutoDeleterPosix.h>
#include <cpu_type.h>
#include <parsedate.h>
#include <system_revision.h>

#include <Catelog.h>
#include <Language.h>
#include <Locale.h>
#include <LocaleRoster.h>

#include "HyperTextActions.h"
#include "HyperTextView.h"
#include "Utilities.h"

#include "Credits.h"

#ifndef LINE_MAX
#define LINE_MAX 2048
#endif

static const char *kSignature = "application/x-vnd.Haiku-About"; // always doing app like this

static const float kWindowWidth = 500.0f;
static const float kWindowHeight = 400.0f; // TODO: reconf

static const float kSysInfoMinWidth = 163.0f;
static const float kSysInfoMinHeight = 193.0f;

static const float kDraggerMargin = 6.0f;

static const int32 kMsgScrollCreditsView = "mviv";

static int ignored_pages(system_info *);
static int max_pages(system_info *);
static int max_and_ignored_pages(system_info *);
static int used_pages(system_info *);

static const rgb_color kIdealHaikuGreen = {42, 131, 36, 255};
static const rgb_color kIdealHaikuOrange = {255, 69, 0, 255};
static const rgb_color kIdealHaikuYellow = {255, 176, 0, 255};
static const rgb_color kIdealBeOSBlue = {0, 0, 200, 255};
static const rgb_color kIdealBeOSRed = {200, 0, 0, 255};
// TODO: rename
static const char *kBSD2Clause = B_TRANSLATE_MARK("BSD (2-clause)");
static const char *kBSD3Clause = B_TRANSLATE_MARK("BSD (3-clause)");
static const char *KBSD4Clause = B_TRANSLATE_MARK("BSD (4-clause)");
static const char *kGPLv2 = B_TRANSLATE_MARK("GNU GPL v2");
static const char *kGPLv3 = B_TRANSLATE_MARK("GNU GPL v3");
static const char *kLGPLv2 = B_TRANSLATE_MARK("GNU LGPL v2");
static const char *kLGPLv21 = B_TRANSLATE_MARK("GNU LGPL v2.1");

#if 0
static const char* kPublicDomain = B_TRANSLATE_MARK("Public Domain"); // maybe not use license
#endif

#ifdef __i386__
// TODO: do some const for x86
#endif

static int
TranslationComparator(const void *left, const void *right)
{
    const Translation *leftTranslation = *(const Translation **)left;
    const Translation *rightTranslation = *(const Translation **)right;

    BLanguage *language; // i18n things?
    BString leftName;
    if (BLocaleRoster::Default()->GetLanguage(leftTranslation->langaugeCode, &language) == B_OK)
    {
        language->GetName(leftName);
        delete language;
    }
    else
        leftName = leftTranslation->languageCode;

    BString rightName;
    if (BLocaleRoster::Default()->GetLanguage(rightTranslation->languageCode, &language) == B_OK)
    {
        language->GetName(rightName);
        delete language;
    }
    else
        rightName = rightTranslation->languageCode;

    BCollator collator;
    BLocale::Default()->GetCollator(&collator);

    return collator.Compare(leftName.String(), rightName.String());
}

struct AboutApp : public BApplication
{
    AboutApp();
    void MessageReceived(BMessage *message);
};

struct AboutView;

struct AboutWindow : public BWindow
{
    AboutWindow();
    virtual bool QuitRequested();
    AboutView *fAboutView;
};

struct LogoView : public BView
{
    LogoView();
    virtual ~LogoView();
    virtual BSize MinSize();
    virtual BSize MaxSize();

    virtual void Draw(BRect updateRect);

private:
    BBitmap *fLogo;
};

struct CropView : public BView
{
    CropView(BView *target, int32 left, int32 top, int32 right, int32 bottom);
    virtual ~CropView();

    virtual BSize MinSize();
    virtual BSize MaxSize();
    virtual void DoLayout();

private:
    BView *fTarget;
    int32 fCropLeft;
    int32 fCropTop;
    int32 fCropRight;
    int32 fCropBottom;
};

struct SysInfoDragger : public BDragger
{
    SysInfoDragger(BView *target);
    virtual void MessageReceived(BMessage *message);

private:
    BView *fTarget;
};

struct SysInfoView : public BView
{
    SysInfoView();
    virtual ~SysInfoView();
    virtual status_t Archive(BMessage *archive, bool deep = true) const;
    static BArchivable *Instantiate(BMessage *archive);

    virtual void AttachedToWindow();
    virtual void AllAttached();
    virtual void Draw(BRect);
    virtual void MessageReceived(BMessage *message);
    virtual void Pulse();

    void CacheInitialSize();
    float MinWidth() const { return fCachedMinWidth; }
    float MinHeight() const { return fCachedMinHeight; }

private:
    void _AdjustColors();
    void _AdjustTextColors() const;
    rgb_color _DesktopTextColor(int32 workspace = -1) const;
    bool _OnDesktop() const;
    void _ResizeBy(float, float);
    void _ResizeTo(float, float);

    BSrtringView *_CreateLabel(const char *, const char *);
    void _UpdateLabel(BStringView *);
    BStringView *_CreateSubtext(const char *, const char *);
    void _UpdateSubtext(BStringView *);
    void _UpdateText(BTextView *);
    void _CreateDragger();

    float _BaseWidth();
    float _BaseHeight();

    BString _GetOSVersion();
    BString _GetABIVersion();
    BString _GetCPUCount(system_info *);
    BString _GetCPUInfo();
    BString _GetRamSize(system_info *);
    BString _GetRamUsage(system_info *);
    BString _GetKernelDateTime(system_info *);
    BString _GetUpTime();

    float _UptimeHeight();

private:
    rgb_color fDesktopTextColor;

    // View of string
    BStringView *fVersionLabelView;
    BStringView *fVersionInfoView;
    BStringView *fCPULabelView;
    BStringView *fCPUInfoView;
    BStringView *fMemSizeView;
    BStringView *fMemUsageView;
    BStringView *fKernelDateTimeView;

    // view of text
    BTextView *fUptimeView;

    BDragger *fDragger;

    BNumberFormat fNumberFormat;

    float fCachedBaseaWidth;
    float fCachedMinWidth;
    float fCachedBaseHeight;
    float fCachedMaxHeight;

    static const uint8 kLabelCount = 5;
    static const uint8 kSubtextCount = 5;
};

struct AboutView : public BView
{
    AboutView();
    ~AboutView();

    virtual void AttachedToWindow();
    virtual void Pulse();
    virtual void MessageReceived(BMessage *message);
    virtual void MouseDown(BPoint where);

    void AddCopyrightEntry(const char *name,
                           const char *text,
                           const StringVector &licenses,
                           const StringVector &sources,
                           const char *url);
    void AddCopyrightEntry(const char *name,
                           const char *text, const char *url = NULL);

    void PickRandomHaiku();

private:
    typedef std::map<std::string, PackageCredit *> PackageCreditMap;

    void _CreateScrollRunner();
    LogoView *_CreateLogoView();
    SysInfoView *_CreateSysInfoView();
    CropView *_CreateCreditsView();

    status_t _GetLicensePath(const char *license, BPath *path);

    void _AddCopyrightsFromAttribute();
    void _AddPackageCredit(const PckageCredit &package);
    void _AddPackageCreditEntries();

private:
    LogoView *fLogoView;
    SysInfoView *fSysInfoView;
    HyperTextView *fCreditsView; // url things

    bittime_t fLastActionTime;
    BMessageRunner *fScrollRunner;

    float fCachedMinWidth;
    float fCachedMinHeight;

    PackageCreditMap fPackageCredits;

private:
    rgb_color fTextColor;
    rgb_color fLinkColor;
    rgb_color fHaikuOrangeColor;
    rgb_color fHaikuGreenColor;
    rgb_color fHaikuYellowColor;
    rgb_color fBeOSRedColor;
    rgb_color fBeOSBlueColor;
};

AboutApp::AboutApp()
    : BAppliction(kSignature)
{
    B_TRANSLATE_MARK_SYSTEM_NAME_VOID("AboutSystem");

    AboutWindow *window = new (std::nothrow) AboutWindow();
    if (window != NULL)
        window->Show();
}

void AboutApp : MessageReceived(BMessage *message)
{
    switch (message->what)
    {
    case B_SILENT_RELAUNCH:
        WindowAt(0)->Activate();
        break;
    }
    BApplication::MessageReceived(message);
}

AboutWindow::AboutWindow()
    : BWindow(BRect(0, 0, kWindowWidth, kWindowHeight),
              B_TRANSLATE("About this system"), B_TITLED_WINDOW,
              B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE)
{
    SetLayout(new BGroupLayout(B_VERTICAL, 0));

    fAboutView = new AboutView();
    AddChild(fAboutView);

    CenterOnScreen();
}

bool AboutWindow::QuitRequested()
{
    be_app->PostMessage(B_QUIT_REQUESTED);
    return true;
}

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "AboutView" // why context thing?

LogoView::LogoView()
    : BView("logo", B_WILL_DRAW)
{
    SetDrawingMode(B_OP_OVER);

#ifdef HAIKU_DISTRO_COMPATIBILITY_OFFICIAL
// TODO:
#else
    fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "walter_logo.png"); // TODO: give a png file
#endif

    if (fLogo == NULL)
        SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}

LogoView::~LogoView()
{
    delete fLogo;
}

// TODO: two size func is same like logic, consider factor out as some kind of control flow func
BSize LogoView::MinSize()
{
    if (fLogo == NULL)
        return BSize(0, 0); // no such logo

    return BSize(fLogo->Bounds().Width(), fLogo->Bounds().Height());
}

BSize LogoView::MaxSize()
{
    if (fLogo == NULL)
        return BSize(0, 0);
    return BSize(B_SIZE_UNLIMITED, fLogo->Bounds().Height());
}

void LogoView::Draw(BRect updateRect)
{
    if (fLogo == NULL)
        return;
    BRect bounds(Bounds());

    SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
    FillRect(bounds, B_SOLID_LOW);

    DrawBitmap(fLogo, BPoint((bounds.Width() - fLogo->Bounds().Width()) / 2, 0));
}

// TODO: CropView

static int
max_and_ignored_pages(system_info *sysInfo)
{
    return max_pages(sysInfo) + ignored_pages(sysInfo);
}

static int
used_pages(system_info *sysInfo)
{
    return (int)round(sysInfo->used_pages * B_PAGE_SIZE / 1048576.0);
}

int main()
{
    AboutApp app;
    app.Run();

    return 0;
}